package com.cs.paoyer.common.exception;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import javax.ejb.EJBException;
//import com.cs.paoyer.common.util.PropertiesLoader;

/**
 * Encargada de generar nuevamente las excepciones User o System que fueron
 * envueltas por el contenedor en una RemoteException.
 *
 * @version 1.0
 */
public class ExceptionAssembler {

    private static final String DELIMITER_TOKEN       = "_[[";
    private static final String JAVA_EXCEPTION_DELIMITER_TOKEN = "JE=[[";
    private static final String CUSTOM_MESSAGE_DELIMITER_TOKEN = "ME=[[";
    private static final String FINAL_DELIMITER_TOKEN = "]]";

    private static final String SYSTEM_EXCEPTION_NAME = "com.cs.paoyer.common.exception.SystemException";
    private static final String USER_EXCEPTION_NAME   = "com.cs.paoyer.common.exception.UserException";

    public static final int DO_NOTHING = -1;
    public static final int USER_EXCEPTION_CLASS = 0;
    public static final int SYSTEM_EXCEPTION_CLASS = 1;

    /**
     * Devuelve la excepci�n que fue lanzada anteriormente desde un m�dulo, y que
     * se perdi� dentro de la excepci�n 'RemoteException' generada en un ambiente
     * distribuido.
     *
     * @param exceptionMessage
     * @param exceptionClass
     * @return
     */
    public static EJBException getException(Exception exception) {
      //Objetos que crean la Instancia de la Clase dinamicamente
      Exception caugthJavaException = null;
      String customMessage = null;

      String exceptionMessage  = exception.getMessage();
      int    exceptionClass    = 0;
      int    codeMaxPos        = 0;
      int    delimiterTokenPos = exceptionMessage.indexOf(DELIMITER_TOKEN);

      String exceptionModule     = null;
      String exceptionCode       = null;
      String exceptionModuleName = null;
      String exceptionClassName  = null;
      final ArrayList modules = new ArrayList();
      modules.add(0,"");
      modules.add(1,"com.cs.paoyer.administration.exception.Administration");
      modules.add(2,"");
      modules.add(3,"");
      modules.add(4,"");
      modules.add(5,"");
      modules.add(6,"");
      modules.add(7,"");
      modules.add(8,"");
      modules.add(9,"com.cs.paoyer.common.exception.Common");

      int numberExceptionCode = 0;

      // El mensaje de la excepci�n tiene el token ...
      if (delimiterTokenPos == -1) {
        return new EJBException(exception);
      }

      if (exceptionMessage.indexOf(SYSTEM_EXCEPTION_NAME) != -1) {
        //System Exception
        exceptionClass = SYSTEM_EXCEPTION_CLASS;
        codeMaxPos = 9;
      } else if (exceptionMessage.indexOf(USER_EXCEPTION_NAME) != -1) {
        //User Exception
        exceptionClass = USER_EXCEPTION_CLASS;
        codeMaxPos = 8;
      } else {
        return new EJBException(exception);
      }

      exceptionModule = exceptionMessage.substring(delimiterTokenPos + 3,
                                                   delimiterTokenPos + 4);
      exceptionCode = exceptionMessage.substring(delimiterTokenPos + 5,
                                                 delimiterTokenPos + codeMaxPos);
   //   exceptionModuleName = PropertiesLoader.getInstance().getProperty(
    //    "EXCEPTION_MODULE_" + exceptionModule);
      exceptionModuleName  = modules.get(Integer.parseInt(exceptionModule)).toString();
      exceptionClassName = getExceptionClassName(exceptionModuleName,  exceptionClass);

      try {
        numberExceptionCode = Integer.parseInt(exceptionCode);
      } catch (NumberFormatException e) {
        return new EJBException(exception);
      }

      switch (exceptionClass) {

        case USER_EXCEPTION_CLASS:
          return new UserException(new ModuleVO(exceptionModule,
                                                exceptionClassName),
                                                numberExceptionCode);

        case SYSTEM_EXCEPTION_CLASS:

        // Si existe, crea la excepci�n de Java generada originalmente.
        try {
          caugthJavaException = (Exception) createJavaException(exceptionMessage);
        } catch (ClassNotFoundException e) {
          return new EJBException(e);
        } catch (IllegalAccessException e) {
          return new EJBException(e);
        } catch (InstantiationException e) {
          return new EJBException(e);
        } catch (SecurityException e) {
          return new EJBException(e);
        } catch (NoSuchMethodException e) {
          return new EJBException(e);
        } catch (InvocationTargetException e) {
          return new EJBException(e);
        } catch (IllegalArgumentException e) {
          return new EJBException(e);
        }

        delimiterTokenPos = exceptionMessage.indexOf(
            CUSTOM_MESSAGE_DELIMITER_TOKEN);
        if (delimiterTokenPos != -1) {
            customMessage = exceptionMessage.substring(
                delimiterTokenPos +
                5,
                exceptionMessage.indexOf(FINAL_DELIMITER_TOKEN,
                                         delimiterTokenPos));
        }

        return new SystemException(new ModuleVO(exceptionModule,
                                                exceptionClassName),
                                                numberExceptionCode,
                                                caugthJavaException,
                                                customMessage, false);

        default:
          return new EJBException(exception);
      }

    }

    /**
     * Devuelve una cadena que representa el nombre de la instancia de la clase
     * que envi� la excepci�n.
     *
     * @param moduleName
     * @param exceptionType
     * @return
     */
    private static String getExceptionClassName(String moduleName,
                                                int exceptionType) {
      String exceptionClassName = moduleName;

      switch (exceptionType) {
        case USER_EXCEPTION_CLASS:
          exceptionClassName = exceptionClassName + "UserException";
          break;
        case SYSTEM_EXCEPTION_CLASS:
          exceptionClassName = exceptionClassName + "SystemException";
          break;
      }

      return exceptionClassName;
    }


    /**
     * Devuelve la excepci�n Java originalmente lanzada por un m�dulo de la aplicaci�n.
     *
     * @param exceptionMessage Mensaje de la excepci�n original.
     * @return Object          Un objeto Excepci�n.
     *
     * @throws java.lang.ClassNotFoundException
     * @throws java.lang.IllegalAccessException
     * @throws java.lang.InstantiationException
     * @throws java.lang.SecurityException
     * @throws java.lang.NoSuchMethodException
     * @throws InvocationTargetException
     * @throws java.lang.IllegalArgumentException
     */
    private static Object createJavaException(String exceptionMessage)
      throws ClassNotFoundException, IllegalAccessException, InstantiationException,
             SecurityException, NoSuchMethodException, InvocationTargetException,
             IllegalArgumentException {

      Exception caugthJavaException    = null;
      Class     exceptionDefinition    = null;
      String    javaExceptionClassName = null;
      String    javaExceptionDescription = null;
      int       delimiterTokenPos;

      // Valores de inicializaci�n.
      Class[] strArgumentsClass = new Class[] {String.class};
      Object[] strArguments     = null;
      Constructor strExceptionConstructor;

      delimiterTokenPos = exceptionMessage.indexOf(JAVA_EXCEPTION_DELIMITER_TOKEN);
      if (delimiterTokenPos != -1) {
        javaExceptionClassName = exceptionMessage.substring(delimiterTokenPos +
          5,
          exceptionMessage.indexOf(FINAL_DELIMITER_TOKEN, delimiterTokenPos));

        delimiterTokenPos = exceptionMessage.indexOf(DELIMITER_TOKEN, delimiterTokenPos + 5);

        javaExceptionDescription = exceptionMessage.substring(
          delimiterTokenPos + 3,
          exceptionMessage.indexOf(FINAL_DELIMITER_TOKEN, delimiterTokenPos));

        strArguments     = new Object[] {javaExceptionDescription};

        // Crea un objeto a trav�s de uno de sus constructores. - Java Reflection
        // 1. Create a Class object for the object you want to create.
        exceptionDefinition = Class.forName(javaExceptionClassName);

        // 2. Create a Constructor object by invoking getConstructor on the Class object.
        strExceptionConstructor = exceptionDefinition.getConstructor(strArgumentsClass);

        // 3. Create the object by invoking newInstance on the Constructor object.
        // Es equivalente a:
        //    caugthJavaException = new Exception(strMessage);
        return caugthJavaException = (Exception) strExceptionConstructor.
          newInstance(strArguments);

      } else {
        return null;
      }
    }


    public static void main(String[] args) {
        String eMessage =
            "RemoteException EJB Exception: ; _[[0_103 ]] nested exception is: \n" +
            "com.etb.saaw.common.exception.UserException:  Conexion JDBC " +
            " del DataSource es NULL. \n" +
            "Ocurrio un error obteniendo una conexion " +
            " JDBC del DataSource.Verifique que los parametros del data source sean " +
            "correctos y que la base de datos y el listener esten funcionando correctamente.";

        throw ExceptionAssembler.getException( new RemoteException( eMessage ) );

    }

}